package org.texteasy;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.DatabaseUtils.InsertHelper;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;
import android.text.TextUtils;

public class MessageContentProvider extends ContentProvider {

	private static final String TAG = "MessageContentProvider";
	
	private static final String DATABASE_NAME = "messages";
	private static final int DATABASE_VERSION = 1;
	
	private DatabaseHelper databaseHelper;
	
	private static HashMap<String, String> defaultMessagesProjectionMap;
	
	private static class DatabaseHelper extends SQLiteOpenHelper{
		
		DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
    	
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    		db.execSQL("create table messages(_id integer primary key autoincrement," +
				"message string not null, created_date not null, finished_date);");
    		db.execSQL("create table message_contacts(_id integer primary key autoincrement," +
				"message_id long not null, contact_id long not null, sent_status not null)");
    	}
    	
    	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            onCreate(db);
    	}
	}
	
	public static final class Constants implements BaseColumns {
		// This class cannot be instantiated
        private Constants() {}
        
        /**
         * The content:// style URL for this table
         */
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/message");

        /**
         * The MIME type of {@link #CONTENT_URI} providing a directory of messages.
         */
        public static final String MESSAGE_CONTENT_TYPE = "vnd.texteasy.cursor.dir/vnd.texteasy.message";

        /**
         * The MIME type of a {@link #CONTENT_URI} sub-directory of a single message.
         */
        public static final String MESSAGE_CONTENT_ITEM_TYPE = "vnd.texteasy.cursor.item/vnd.texteasy.message";

        /**
         * The content:// style URL for this table
         */
        public static final Uri CONTACT_MESSAGE_CONTENT_URI = Uri.parse("content://" + CONTACT_MESSAGE_AUTHORITY+ "/contactmessage");
        
        /**
         * The MIME type of {@link #CONTENT_URI} providing a directory of messages.
         */
        public static final String CONTACT_MESSAGE_CONTENT_TYPE = "vnd.texteasy.cursor.dir/vnd.texteasy.contactmessage";

        /**
         * The MIME type of a {@link #CONTENT_URI} sub-directory of a single message.
         */
        public static final String CONTACT_MESSAGE_CONTENT_ITEM_TYPE = "vnd.texteasy.cursor.item/vnd.texteasy.contactmessage";
        
        /**
         * The default sort order for this table
         */
        public static final String DEFAULT_SORT_ORDER = "created_date DESC";
        
        /**
         * Table name of the messages table
         */
        public static final String TABLE_NAME_MESSAGES= "messages";
  
        /**
         * Table name of the message_contacts table
         */
        public static final String TABLE_NAME_MESSAGE_CONTACTS= "message_contacts";
        
        /**
         * Name of the column in the MESSAGES table that contains the actual message
         */
        public static final String COLUMN_MESSAGE="message";
        
        /**
         * Name of the column in the MESSAGES table that contains the date the message was created.
         */
        public static final String COLUMN_CREATED_DATE="created_date";
        
        /**
         * Name of the column in the MESSAGES table that contains the date the message 
         * finished sending/cancelling/whatever
         */
        public static final String COLUMN_FINISHED_DATE="finished_date";
        
        /**
         * Name of the column in the MESSAGE_CONTACTS table that references the message
         * to send to the contact.
         */
        public static final String COLUMN_MESSAGE_ID="message_id";
        
        /**
         * Name of the column in the MESSAGE_CONTACTS table that references the contact
         * to send the message to
         */
        public static final String COLUMN_CONTACT_ID="contact_id";
        
        /**
         * Name of the column in the MESSAGE_CONTACTS table that indicates
         * if the message was sent, canceled, in_progress for that contact
         */
        public static final String COLUMN_SENT_STATUS="sent_status";
        
        public static final String STATUS_IN_PROGRESS="IN PROGRESS";
        public static final String STATUS_CANCELED="CANCELED";
        public static final String STATUS_SENT="SENT";
        
	}
	
	private static final UriMatcher sUriMatcher;
	public static final String AUTHORITY = "org.texteasy.BulkMessenger.Message";
	public static final String CONTACT_MESSAGE_AUTHORITY = "org.texteasy.BulkMessenger.ContactMessage";
	
	
    private static final int MESSAGES = 1;
    private static final int MESSAGE = 2;
    private static final int CONTACT_MESSAGES = 3;
    private static final int CONTACT_MESSAGE = 4;
	
	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		
		return 0;
	}

	@Override
	public String getType(Uri uri) {
		switch (sUriMatcher.match(uri)) {
        case MESSAGES:
            return Constants.MESSAGE_CONTENT_TYPE;
        case MESSAGE:
            return Constants.MESSAGE_CONTENT_ITEM_TYPE;
        case CONTACT_MESSAGES:
        	return Constants.CONTACT_MESSAGE_CONTENT_TYPE;
        case CONTACT_MESSAGE:
        	return Constants.CONTACT_MESSAGE_CONTENT_ITEM_TYPE;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		
		if (sUriMatcher.match(uri) != MESSAGES) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
		
		ContentValues values;
        if (initialValues != null) {
            values = new ContentValues(initialValues);
        } else {
            values = new ContentValues();
        }
        
        //expecting a message to send and a list of contacts
        //to send the message to
        String message = values.getAsString(Constants.COLUMN_MESSAGE);
        if(message == null){
        	throw new IllegalArgumentException("Must have a value for the key: " + 
    			Constants.COLUMN_MESSAGE);
        }
		
        SQLiteDatabase database = databaseHelper.getWritableDatabase();
    	InsertHelper messagesInsertHelper = new InsertHelper(database, Constants.TABLE_NAME_MESSAGES);
    	InsertHelper messageContactsInsertHelper = new InsertHelper(database, 
			Constants.TABLE_NAME_MESSAGE_CONTACTS);
        database.beginTransaction();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        long messageId = -1;
        try{
        	ContentValues newValue = new ContentValues();
        	newValue.put(Constants.COLUMN_MESSAGE, message);
        	newValue.put(Constants.COLUMN_CREATED_DATE, dateFormat.format(new Date()));
        	messageId = messagesInsertHelper.insert(newValue);
        	
        	//now get the contactIds to associate with the message
        	for(Entry<String, Object> entries: values.valueSet()){
        		if(!entries.getKey().startsWith(Constants.COLUMN_CONTACT_ID)){
        			continue;
        		}
        		
        		newValue = new ContentValues();
        		newValue.put(Constants.COLUMN_MESSAGE_ID, messageId);
        		newValue.put(Constants.COLUMN_CONTACT_ID, entries.getValue().toString());
        		newValue.put(Constants.COLUMN_SENT_STATUS, Constants.STATUS_IN_PROGRESS);
        		messageContactsInsertHelper.insert(newValue);
        	}
	    	database.setTransactionSuccessful();
    	}finally{
    		database.endTransaction();
    	}
    	
    	database.close();
    	
    	if(messageId > 0){
	    	Uri messageUri = ContentUris.withAppendedId(Constants.CONTENT_URI, messageId);
	        getContext().getContentResolver().notifyChange(messageUri, null);
	        return messageUri;
    	}
    	
    	throw new SQLException("Failed to insert row into " + uri);
	}

	/**
	 * Inserts a new message
	 * @param initialValues
	 * @return
	 */
	private Uri insertMessage(Uri uri,ContentValues values){
		
		/*ArrayList<Long> contactIds = new ArrayList<Long>();
		String message = null;
		for(Entry<String, Object> entries: values.valueSet()){
			if(entries.getKey().startsWith(Constants.MESSAGE_CONTACT_ID)){
				contactIds.add((Long)entries.getValue());
			}else if(entries.getKey().equals(Constants.MESSAGE_TEXT)){
				message = (String)entries.getValue();
			}
		}
		
		if(message == null){
			throw new IllegalArgumentException("A " + Constants.MESSAGE_TEXT+ " value is required " +
				"to insert a message");
		}else if(contactIds.isEmpty()){
			throw new IllegalArgumentException("At least one " + Constants.MESSAGE_CONTACT_ID+ " value is required " +
				"to insert a message");
		}
		
		
		/*long rowId = this.dbAdaptor.createNewMessage(message, contactIds);
        if (rowId > 0) {
            Uri messageUri = ContentUris.withAppendedId(Constants.CONTENT_URI, rowId);
            getContext().getContentResolver().notifyChange(messageUri, null);
            return messageUri;
        }*/
        throw new SQLException("Failed to insert row into " + uri);
	}
	
	
	@Override
	public boolean onCreate() {
		this.databaseHelper = new DatabaseHelper(getContext());
		return false;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		
		SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
		queryBuilder.setTables(Constants.TABLE_NAME_MESSAGES + " LEFT OUTER JOIN " + Constants.TABLE_NAME_MESSAGE_CONTACTS + 
	        	" ON " + Constants.TABLE_NAME_MESSAGES + "." + Constants._ID + "=" + Constants.TABLE_NAME_MESSAGE_CONTACTS + "." + 
	        	Constants.COLUMN_MESSAGE_ID);
        queryBuilder.setProjectionMap(defaultMessagesProjectionMap);
		switch (sUriMatcher.match(uri)) {
        case MESSAGES:
            break;

        case MESSAGE:
        	queryBuilder.appendWhere(Constants.TABLE_NAME_MESSAGES + "." + 
            		Constants._ID + "=" + uri.getPathSegments().get(1));
            break;

        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
		
		// If no sort order is specified use the default
        String orderBy;
        if (TextUtils.isEmpty(sortOrder)) {
            orderBy = Constants.DEFAULT_SORT_ORDER;
        } else {
            orderBy = sortOrder;
        }

        SQLiteDatabase db = databaseHelper.getReadableDatabase();
        Cursor c = queryBuilder.query(db, projection, selection, selectionArgs, null, null, orderBy);

        // Tell the cursor what uri to watch, so it knows when its source data changes
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		
		SQLiteDatabase db = databaseHelper.getWritableDatabase();
        int count;
        
		switch (sUriMatcher.match(uri)) {
        case MESSAGES:
        	count = db.update(Constants.TABLE_NAME_MESSAGES, values, selection, selectionArgs);
            break;

        case MESSAGE:
        	String messageId = uri.getPathSegments().get(1);
        	//there is only one update that can be performed on a message and that
        	//is to set the finished_date, but can only do that if all
        	//its contact_message rows are in STATUS_CANCELED or STATUS_SENT
        	Cursor messageContactsCount = db.rawQuery("select count(*) from message_contacts " +
        			"where message_id = ? and sent_status not in ('" +Constants.STATUS_CANCELED + 
        			"', '" +Constants.STATUS_SENT +"')", new String[]{messageId});
        	
        	messageContactsCount.moveToNext();
        	if(messageContactsCount.getInt(0) > 0){
        		throw new IllegalStateException("Failed to update the finished date for the message: " + 
        				messageId + ". It still has unprocessed message_contacts");
        	}
        	
        	//update messages set finished_date = ? where _id = ?
            count = db.update(Constants.TABLE_NAME_MESSAGES, values, 
            		Constants.TABLE_NAME_MESSAGES + "." + Constants._ID + "=" + messageId
                    + (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""),
                    selectionArgs);
            break;

        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
		
		getContext().getContentResolver().notifyChange(uri, null);
        return count;
	}

	
	static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, "message", MESSAGES);
        sUriMatcher.addURI(AUTHORITY, "message/#", MESSAGE);
        sUriMatcher.addURI(CONTACT_MESSAGE_AUTHORITY, "contactmessage", MESSAGES);
        sUriMatcher.addURI(CONTACT_MESSAGE_AUTHORITY, "contactmessage/#", MESSAGE);
        
        defaultMessagesProjectionMap = new HashMap<String, String>();
        defaultMessagesProjectionMap.put(Constants.TABLE_NAME_MESSAGES + "." + Constants._ID , Constants.TABLE_NAME_MESSAGES + "." + Constants._ID);
        //defaultMessagesProjectionMap.put(Constants._ID , Constants._ID);
        defaultMessagesProjectionMap.put(Constants.COLUMN_CONTACT_ID , Constants.COLUMN_CONTACT_ID);
        defaultMessagesProjectionMap.put(Constants.COLUMN_CREATED_DATE , Constants.COLUMN_CREATED_DATE);
        defaultMessagesProjectionMap.put(Constants.COLUMN_FINISHED_DATE , Constants.COLUMN_FINISHED_DATE);
        defaultMessagesProjectionMap.put(Constants.COLUMN_MESSAGE , Constants.COLUMN_MESSAGE);
        defaultMessagesProjectionMap.put(Constants.COLUMN_SENT_STATUS , Constants.COLUMN_SENT_STATUS);
        
    }
}
